<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="zh-CN">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2"/>
<meta name="theme-color" content="#222">












<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />






















<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=6.1.0" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=6.1.0">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=6.1.0">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=6.1.0">


  <link rel="mask-icon" href="/images/logo.svg?v=6.1.0" color="#222">









<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Gemini',
    version: '6.1.0',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: false,
    fastclick: false,
    lazyload: false,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>


  




  <meta name="description" content="自定义收集器之前我们简单说过了Collector接口，以及他的简单使用，现在我们来尝试自定义一个收集器，来进行更加深刻的理解。 Collector的5个方法： 123456789Supplier&amp;lt;A&amp;gt; supplier();    BiConsumer&amp;lt;A, T&amp;gt; accumulator();BinaryOperator&amp;lt;A&amp;gt; combiner();Functi">
<meta name="keywords" content="java,jdk8">
<meta property="og:type" content="article">
<meta property="og:title" content="JDK8-自定义收集器和注意事项">
<meta property="og:url" content="http://www.saily.top/2017/02/26/jdk8-7/index.html">
<meta property="og:site_name" content="帆的博客">
<meta property="og:description" content="自定义收集器之前我们简单说过了Collector接口，以及他的简单使用，现在我们来尝试自定义一个收集器，来进行更加深刻的理解。 Collector的5个方法： 123456789Supplier&amp;lt;A&amp;gt; supplier();    BiConsumer&amp;lt;A, T&amp;gt; accumulator();BinaryOperator&amp;lt;A&amp;gt; combiner();Functi">
<meta property="og:locale" content="zh-CN">
<meta property="og:updated_time" content="2017-03-01T05:29:22.000Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="JDK8-自定义收集器和注意事项">
<meta name="twitter:description" content="自定义收集器之前我们简单说过了Collector接口，以及他的简单使用，现在我们来尝试自定义一个收集器，来进行更加深刻的理解。 Collector的5个方法： 123456789Supplier&amp;lt;A&amp;gt; supplier();    BiConsumer&amp;lt;A, T&amp;gt; accumulator();BinaryOperator&amp;lt;A&amp;gt; combiner();Functi">



  <link rel="alternate" href="/atom.xml" title="帆的博客" type="application/atom+xml" />




  <link rel="canonical" href="http://www.saily.top/2017/02/26/jdk8-7/"/>



<script type="text/javascript" id="page.configurations">
  CONFIG.page = {
    sidebar: "",
  };
</script>

  <title>JDK8-自定义收集器和注意事项 | 帆的博客</title>
  









  <noscript>
  <style type="text/css">
    .use-motion .motion-element,
    .use-motion .brand,
    .use-motion .menu-item,
    .sidebar-inner,
    .use-motion .post-block,
    .use-motion .pagination,
    .use-motion .comments,
    .use-motion .post-header,
    .use-motion .post-body,
    .use-motion .collection-title { opacity: initial; }

    .use-motion .logo,
    .use-motion .site-title,
    .use-motion .site-subtitle {
      opacity: initial;
      top: initial;
    }

    .use-motion {
      .logo-line-before i { left: initial; }
      .logo-line-after i { right: initial; }
    }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-CN">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">帆的博客</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">扬帆起航</p>
      
  </div>

  <div class="site-nav-toggle">
    <button aria-label="切换导航栏">
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>




<nav class="site-nav">
  
    <ul id="menu" class="menu">
      
        
        
        
          
          <li class="menu-item menu-item-home">
    <a href="/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-home"></i> <br />首页</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-about">
    <a href="/about/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-user"></i> <br />关于</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-tags">
    <a href="/tags/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />标签</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-categories">
    <a href="/categories/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-th"></i> <br />分类</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-archives">
    <a href="/archives/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />归档</a>
  </li>

      
      
        <li class="menu-item menu-item-search">
          
            <a href="javascript:;" class="popup-trigger">
          
            
              <i class="menu-item-icon fa fa-search fa-fw"></i> <br />搜索</a>
        </li>
      
    </ul>
  

  

  
    <div class="site-search">
      
  <div class="popup search-popup local-search-popup">
  <div class="local-search-header clearfix">
    <span class="search-icon">
      <i class="fa fa-search"></i>
    </span>
    <span class="popup-btn-close">
      <i class="fa fa-times-circle"></i>
    </span>
    <div class="local-search-input-wrapper">
      <input autocomplete="off"
             placeholder="搜索..." spellcheck="false"
             type="text" id="local-search-input">
    </div>
  </div>
  <div id="local-search-result"></div>
</div>



    </div>
  
</nav>



  



</div>
    </header>

    


    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          
            

          
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://www.saily.top/2017/02/26/jdk8-7/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="杨帆">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/img/photo/bug.png">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="帆的博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">JDK8-自定义收集器和注意事项
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">
            
                
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2017-02-26T14:10:04+08:00">2月 26 2017</time>
            

            
            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/jdk8/" itemprop="url" rel="index"><span itemprop="name">jdk8</span></a></span>

                
                
              
            </span>
          

          
            
          

          
          

          
            <span class="post-meta-divider">|</span>
            <span class="post-meta-item-icon"
            >
            <i class="fa fa-eye"></i>
             阅读次数： 
            <span class="busuanzi-value" id="busuanzi_value_page_pv" ></span>
            </span>
          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <h3 id="自定义收集器"><a href="#自定义收集器" class="headerlink" title="自定义收集器"></a>自定义收集器</h3><p>之前我们简单说过了Collector接口，以及他的简单使用，现在我们来尝试自定义一个收集器，来进行更加深刻的理解。</p>
<p>Collector的5个方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">Supplier&lt;A&gt; <span class="title">supplier</span><span class="params">()</span></span>;    </span><br><span class="line"><span class="function">BiConsumer&lt;A, T&gt; <span class="title">accumulator</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">BinaryOperator&lt;A&gt; <span class="title">combiner</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">Function&lt;A, R&gt; <span class="title">finisher</span><span class="params">()</span></span>;</span><br><span class="line"><span class="comment">// 收集器特性，只有3个值，CONCURRENT，UNORDERED，IDENTITY\_FINISH</span></span><br><span class="line"><span class="comment">// CONCURRENT标识同一个结果容器可以由多个线程多次调用。</span></span><br><span class="line"><span class="comment">// UNORDERED标识收集器并不承诺保证流的顺序。</span></span><br><span class="line"><span class="comment">// IDENTITY_FINISH标识finisher函数就是identity函数。</span></span><br><span class="line"><span class="function">Set&lt;Characteristics&gt; <span class="title">characteristics</span><span class="params">()</span></span>;</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<p>了解以上接口方法后，我们来自己实现一个收集器：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MySetCollector</span>&lt;<span class="title">T</span>&gt; <span class="keyword">implements</span> <span class="title">Collector</span>&lt;<span class="title">T</span>, <span class="title">Set</span>&lt;<span class="title">T</span>&gt;, <span class="title">Set</span>&lt;<span class="title">T</span>&gt;&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 产生结果容器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Supplier&lt;Set&lt;T&gt;&gt; supplier() &#123;</span><br><span class="line">        System.out.println(<span class="string">"supplier invoked!"</span>);</span><br><span class="line">        <span class="keyword">return</span> HashSet::<span class="keyword">new</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 累加器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> BiConsumer&lt;Set&lt;T&gt;, T&gt; accumulator() &#123;</span><br><span class="line">        System.out.println(<span class="string">"accumulator invoked!"</span>);</span><br><span class="line">        <span class="keyword">return</span> Set&lt;T&gt;::add;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 用于并行流，将多个部分的执行结果合并起来</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> BinaryOperator&lt;Set&lt;T&gt;&gt; combiner() &#123;</span><br><span class="line">        System.out.println(<span class="string">"combiner invoked!"</span>);</span><br><span class="line">        <span class="keyword">return</span> (set1, set2) -&gt; &#123;</span><br><span class="line">            set1.addAll(set2);</span><br><span class="line">            <span class="keyword">return</span> set1;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 合并后返回最终的结果类型</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Function&lt;Set&lt;T&gt;, Set&lt;T&gt;&gt; finisher() &#123;</span><br><span class="line">        System.out.println(<span class="string">"finisher invoked!"</span>);</span><br><span class="line">        <span class="keyword">return</span> Function.identity();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Set&lt;Characteristics&gt; <span class="title">characteristics</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"characteristics invoked"</span>);</span><br><span class="line">        <span class="keyword">return</span> Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, UNORDERED));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        List&lt;String&gt; list = Arrays.asList(<span class="string">"hello"</span>, <span class="string">"world"</span>, <span class="string">"welcome"</span>);</span><br><span class="line"></span><br><span class="line">        Set&lt;String&gt; set = list.stream().collect(<span class="keyword">new</span> MySetCollector&lt;&gt;());</span><br><span class="line"></span><br><span class="line">        System.out.println(set);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>输出结果如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">supplier invoked!</span><br><span class="line">accumulator invoked!</span><br><span class="line">combiner invoked!</span><br><span class="line">characteristics invoked</span><br><span class="line">characteristics invoked</span><br><span class="line">[world, hello, welcome]</span><br></pre></td></tr></table></figure>
<p>首先说明一点，因为这些返回都是函数式接口，所以方法被调用了，也不意味着行为被执行了。</p>
<p>我们来看一下collect方法的源码。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> &lt;R, A&gt; <span class="function">R <span class="title">collect</span><span class="params">(Collector&lt;? <span class="keyword">super</span> P_OUT, A, R&gt; collector)</span> </span>&#123;</span><br><span class="line">    A container;</span><br><span class="line">    <span class="keyword">if</span> (isParallel()</span><br><span class="line">            &amp;&amp; (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))</span><br><span class="line">            &amp;&amp; (!isOrdered() || collector.characteristics().contains(Collector.Characteristics.UNORDERED))) &#123;</span><br><span class="line">        container = collector.supplier().get();</span><br><span class="line">        BiConsumer&lt;A, ? <span class="keyword">super</span> P_OUT&gt; accumulator = collector.accumulator();</span><br><span class="line">        forEach(u -&gt; accumulator.accept(container, u));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        container = evaluate(ReduceOps.makeRef(collector));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)</span><br><span class="line">           ? (R) container</span><br><span class="line">           : collector.finisher().apply(container);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>我们的不是并行流，就直接看evaluate(ReduceOps.makeRef(collector));这一行代码，我们再进ReduceOps.makeRef看一下源码。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;T, I&gt; TerminalOp&lt;T, I&gt;</span><br><span class="line">    makeRef(Collector&lt;? <span class="keyword">super</span> T, I, ?&gt; collector) &#123;</span><br><span class="line">    Supplier&lt;I&gt; supplier = Objects.requireNonNull(collector).supplier();</span><br><span class="line">    BiConsumer&lt;I, ? <span class="keyword">super</span> T&gt; accumulator = collector.accumulator();</span><br><span class="line">    BinaryOperator&lt;I&gt; combiner = collector.combiner();</span><br><span class="line">    <span class="class"><span class="keyword">class</span> <span class="title">ReducingSink</span> <span class="keyword">extends</span> <span class="title">Box</span>&lt;<span class="title">I</span>&gt;</span></span><br><span class="line"><span class="class">            <span class="keyword">implements</span> <span class="title">AccumulatingSink</span>&lt;<span class="title">T</span>, <span class="title">I</span>, <span class="title">ReducingSink</span>&gt; </span>&#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">begin</span><span class="params">(<span class="keyword">long</span> size)</span> </span>&#123;</span><br><span class="line">            state = supplier.get();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">accept</span><span class="params">(T t)</span> </span>&#123;</span><br><span class="line">            accumulator.accept(state, t);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">combine</span><span class="params">(ReducingSink other)</span> </span>&#123;</span><br><span class="line">            state = combiner.apply(state, other.state);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> ReduceOp&lt;T, I, ReducingSink&gt;(StreamShape.REFERENCE) &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> ReducingSink <span class="title">makeSink</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> ReducingSink();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getOpFlags</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> collector.characteristics().contains(Collector.Characteristics.UNORDERED)</span><br><span class="line">                   ? StreamOpFlag.NOT_ORDERED</span><br><span class="line">                   : <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>可以看到下面的代码就已经执行了这3个方法，来获取3个函数式接口。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Supplier&lt;I&gt; supplier = Objects.requireNonNull(collector).supplier();</span><br><span class="line">BiConsumer&lt;I, ? <span class="keyword">super</span> T&gt; accumulator = collector.accumulator();</span><br><span class="line">BinaryOperator&lt;I&gt; combiner = collector.combiner();</span><br></pre></td></tr></table></figure>
<p>那么为什么characteristics()方法调用了2次呢，看看上面的源码就知道，一处是在getOpFlags方法调用的，一处是在collect的最后一行代码里调用的。</p>
<p>如果Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, UNORDERED));把IDENTITY_FINISH去掉，那么就会调用finisher方法。</p>
<h3 id="自定义收集器深度剖析和并行流陷阱"><a href="#自定义收集器深度剖析和并行流陷阱" class="headerlink" title="自定义收集器深度剖析和并行流陷阱"></a>自定义收集器深度剖析和并行流陷阱</h3><p>接下里我们自定义的一个Collector目的是要把一个list转换成一个map，不过要求Supplier返回是一个Set。意思就是Collector&lt;T,A,R&gt;3个泛型参数，A和R的类型是不一样的。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MySetCollector2</span>&lt;<span class="title">T</span>&gt; <span class="keyword">implements</span> <span class="title">Collector</span>&lt;<span class="title">T</span>, <span class="title">Set</span>&lt;<span class="title">T</span>&gt;, <span class="title">Map</span>&lt;<span class="title">T</span>, <span class="title">T</span>&gt;&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 产生结果容器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Supplier&lt;Set&lt;T&gt;&gt; supplier() &#123;</span><br><span class="line">        System.out.println(<span class="string">"supplier invoked!"</span>);</span><br><span class="line">        <span class="keyword">return</span> HashSet::<span class="keyword">new</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 累加器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> BiConsumer&lt;Set&lt;T&gt;, T&gt; accumulator() &#123;</span><br><span class="line">        System.out.println(<span class="string">"accumulator invoked!"</span>);</span><br><span class="line">        <span class="keyword">return</span> (set, item) -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">"accumulator: "</span> + set +  Thread.currentThread().getName());</span><br><span class="line">            set.add(item);</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 用于并行流，将多个部分的执行结果合并起来</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> BinaryOperator&lt;Set&lt;T&gt;&gt; combiner() &#123;</span><br><span class="line">        System.out.println(<span class="string">"combiner invoked!"</span>);</span><br><span class="line">        <span class="keyword">return</span> (set1, set2) -&gt; &#123;</span><br><span class="line">            set1.addAll(set2);</span><br><span class="line">            <span class="keyword">return</span> set1;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 合并后返回最终的结果类型</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Function&lt;Set&lt;T&gt;, Map&lt;T, T&gt;&gt; finisher() &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> set -&gt; &#123;</span><br><span class="line">            Map&lt;T, T&gt; map = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line">            set.forEach(item -&gt; map.put(item, item));</span><br><span class="line">            <span class="keyword">return</span> map;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Set&lt;Characteristics&gt; <span class="title">characteristics</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"characteristics invoked"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 这里就不能再写IDENTITY_FINISH了，因为A和R的类型不一样，如果写了会报一个强制转换的异常。</span></span><br><span class="line">        <span class="keyword">return</span> Collections.unmodifiableSet(EnumSet.of(UNORDERED));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        List&lt;String&gt; list = Arrays.asList(<span class="string">"hello"</span>, <span class="string">"world"</span>, <span class="string">"welcome"</span>, <span class="string">"hello"</span>, <span class="string">"a"</span>, <span class="string">"b"</span>, <span class="string">"c"</span>, <span class="string">"d"</span>, <span class="string">"e"</span>, <span class="string">"f"</span>, <span class="string">"g"</span>);</span><br><span class="line"></span><br><span class="line">        Map&lt;String, String&gt; map = list.stream().collect(<span class="keyword">new</span> MySetCollector2&lt;&gt;());</span><br><span class="line"></span><br><span class="line">        System.out.println(map);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>输出结果：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">supplier invoked!</span><br><span class="line">accumulator invoked!</span><br><span class="line">combiner invoked!</span><br><span class="line">characteristics invoked</span><br><span class="line">characteristics invoked</span><br><span class="line">finisher invoked</span><br><span class="line">&#123;a=a, b=b, world=world, c=c, d=d, e=e, f=f, g=g, hello=hello, welcome=welcome&#125;</span><br></pre></td></tr></table></figure>
<p>上面写到不能再添加IDENTITY_FINISH，否则会出错，原因就在于之前我们解释的过的源码中的一行代码，就是collect的源码中。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)</span><br><span class="line">               ? (R) container</span><br><span class="line">               : collector.finisher().apply(container);</span><br></pre></td></tr></table></figure>
<p>很明显，如果包含了IDENTITY_FINISH枚举，那么会执行(R) container。直接进行强制转换，在我们这里的container的e类型是Set,R是Map，那么这行代码就是把Set强制转换成一个Map，当然我们就会得到一个强制类型转换的异常。这也说明一个问题，characteristics并不能乱写，所以我们要理解这个函数的每一个枚举的含义，才能在开发中很好的运用。</p>
<p>接下来我们进行一个改造，把stream换成parallelStream。<br>运行一次</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">characteristics invoked</span><br><span class="line">supplier invoked!</span><br><span class="line">accumulator invoked!</span><br><span class="line">combiner invoked!</span><br><span class="line">characteristics invoked</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-3</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-3</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-3</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-3</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-3</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-3</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-3</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-3</span><br><span class="line">accumulator: []main</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-1</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-2</span><br><span class="line">characteristics invoked</span><br><span class="line">finisher invoked</span><br><span class="line">&#123;a=a, b=b, world=world, c=c, d=d, e=e, f=f, g=g, hello=hello, welcome=welcome&#125;</span><br></pre></td></tr></table></figure>
<p>我们可以看到有多个线程进行了累加器的调用。<br>我们再改一行代码</p>
<pre><code>return Collections.unmodifiableSet(EnumSet.of(UNORDERED, CONCURRENT);
</code></pre><p>输出：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">characteristics invoked</span><br><span class="line">characteristics invoked</span><br><span class="line">supplier invoked!</span><br><span class="line">accumulator invoked!</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-<span class="number">1</span></span><br><span class="line">accumulator: [welcome]ForkJoinPool.commonPool-worker-<span class="number">1</span></span><br><span class="line">accumulator: [a, welcome]ForkJoinPool.commonPool-worker-<span class="number">1</span></span><br><span class="line">accumulator: [a, hello, welcome]ForkJoinPool.commonPool-worker-<span class="number">1</span></span><br><span class="line">accumulator: [a, f, hello, welcome]ForkJoinPool.commonPool-worker-<span class="number">1</span></span><br><span class="line">accumulator: [a, f, g, hello, welcome]ForkJoinPool.commonPool-worker-<span class="number">1</span></span><br><span class="line">accumulator: [a, e, f, g, hello, welcome]ForkJoinPool.commonPool-worker-<span class="number">1</span></span><br><span class="line">accumulator: [a, b, e, f, g, hello, welcome]ForkJoinPool.commonPool-worker-<span class="number">1</span></span><br><span class="line">accumulator: [a, hello, welcome]main</span><br><span class="line">accumulator: [a, hello, welcome]ForkJoinPool.commonPool-worker-<span class="number">2</span></span><br><span class="line">accumulator: [a, hello, welcome]ForkJoinPool.commonPool-worker-<span class="number">3</span></span><br><span class="line">characteristics invoked</span><br><span class="line">finisher invoked</span><br><span class="line">&#123;a=a, b=b, c=c, world=world, d=d, e=e, f=f, g=g, hello=hello, welcome=welcome&#125;</span><br></pre></td></tr></table></figure>
<p>这有个很明显的差别，就是累加器输出的时候，打印set的值[]差别很大，这个不是偶然的，我们看CONCURRENT的api解释：<strong>允许有多个线程操作同一个结果容器，并且只能被用于无序(UNORDERED)的流</strong>。反过来想一下，如果没有加CONCURRENT特性，那么并行流就是有几个线程，就有几个结果容器被操作了，</p>
<p>我们多执行几次代码，可能会得到一个<code>ConcurrentModificationException</code>的异常，可是，如果我们把测试代码中打印set值的代码去掉，无论你执行多少次，也不会出现这个异常。<br>我们看一下<code>ConcurrentModificationException</code>的说明： This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible. For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it.<br>意思很明确了，不允许一个线程在修改的时候，另一个线程同时又在迭代它。所以我们要在实际开发中，避免在累加器中对中间结果容器进行额外的操作。</p>
<p>那么如何证明加了CONCURRENT之后就只有一个中间结果容器，不加就就有多个中间结果容器呢(多个线程多个容器，所以不会有ConcurrentModificationException)，用combiner就能测试了，因为只有一个中间结果容器的话，combiner根本不会执行。</p>
<p>我们把代码改一下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> BinaryOperator&lt;Set&lt;T&gt;&gt; combiner() &#123;</span><br><span class="line">    System.out.println(<span class="string">"combiner invoked!"</span>);</span><br><span class="line">    <span class="keyword">return</span> (set1, set2) -&gt; &#123;</span><br><span class="line">        System.out.println(<span class="string">"set1: "</span> + set1);</span><br><span class="line">        System.out.println(<span class="string">"set1: "</span> + set2);</span><br><span class="line">        set1.addAll(set2);</span><br><span class="line">        <span class="keyword">return</span> set1;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>不加CONCURRENT:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">characteristics invoked</span><br><span class="line">supplier invoked!</span><br><span class="line">accumulator invoked!</span><br><span class="line">combiner invoked!</span><br><span class="line">characteristics invoked</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-1 hashcode: 0</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-2 hashcode: 0</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-1 hashcode: 0</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-3 hashcode: 0</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-1 hashcode: 0</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-3 hashcode: 0</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-3 hashcode: 0</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-2 hashcode: 0</span><br><span class="line">set1: [hello]</span><br><span class="line">set1: [hello]</span><br><span class="line">set1: [world]</span><br><span class="line">set1: [f]</span><br><span class="line">set1: [g]</span><br><span class="line">set1: [a]</span><br><span class="line">set1: [welcome]</span><br><span class="line">set1: [e]</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-3 hashcode: 0</span><br><span class="line">set1: [f, g]</span><br><span class="line">set1: [a, hello]</span><br><span class="line">set1: [world, hello]</span><br><span class="line">set1: [a, hello, welcome]</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-3 hashcode: 0</span><br><span class="line">accumulator: []main hashcode: 0</span><br><span class="line">set1: [c]</span><br><span class="line">set1: [d]</span><br><span class="line">set1: [b]</span><br><span class="line">set1: [c, d]</span><br><span class="line">set1: [b, c, d]</span><br><span class="line">set1: [e, f, g]</span><br><span class="line">set1: [a, world, hello, welcome]</span><br><span class="line">set1: [b, c, d, e, f, g]</span><br><span class="line">characteristics invoked</span><br><span class="line">finisher invoked</span><br><span class="line">&#123;a=a, b=b, world=world, c=c, d=d, e=e, f=f, g=g, hello=hello, welcome=welcome&#125;</span><br></pre></td></tr></table></figure>
<p>加了CONCURRENT之后：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">characteristics invoked</span><br><span class="line">characteristics invoked</span><br><span class="line">supplier invoked!</span><br><span class="line">accumulator invoked!</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-1 hashcode: 0</span><br><span class="line">accumulator: [welcome]ForkJoinPool.commonPool-worker-1 hashcode: 1233099618</span><br><span class="line">accumulator: [a, welcome]ForkJoinPool.commonPool-worker-1 hashcode: 1233099715</span><br><span class="line">accumulator: []main hashcode: 0</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-2 hashcode: 0</span><br><span class="line">accumulator: []ForkJoinPool.commonPool-worker-3 hashcode: 0</span><br><span class="line">accumulator: [a, world, hello, welcome]ForkJoinPool.commonPool-worker-2 hashcode: 1445580839</span><br><span class="line">accumulator: [a, hello, welcome]ForkJoinPool.commonPool-worker-1 hashcode: 1332262037</span><br><span class="line">accumulator: [a, world, e, hello, welcome]ForkJoinPool.commonPool-worker-2 hashcode: 1445580940</span><br><span class="line">accumulator: [a, world, hello, welcome]ForkJoinPool.commonPool-worker-3 hashcode: 1445580839</span><br><span class="line">accumulator: [a, world, e, f, hello, welcome]ForkJoinPool.commonPool-worker-1 hashcode: 1445581042</span><br><span class="line">characteristics invoked</span><br><span class="line">finisher invoked</span><br><span class="line">&#123;a=a, b=b, world=world, c=c, d=d, e=e, f=f, g=g, hello=hello, welcome=welcome&#125;</span><br></pre></td></tr></table></figure>
<p>可以看到的，加了CONCURRENT之后根本就没有执行combiner方法。<br>所以我们可以再总结一下combiner的使用说明，就是在并行流，并且收集器的特性没有CONCURRENT特性的时候，combiner才会被调用。</p>

      
    </div>

    

    
    
    

    

    
      <div>
        <div style="padding: 10px 0; margin: 20px auto; width: 90%; text-align: center;">
  <div>坚持原创技术分享，您的支持将鼓励我继续创作！</div>
  <button id="rewardButton" disable="enable" onclick="var qr = document.getElementById('QR'); if (qr.style.display === 'none') {qr.style.display='block';} else {qr.style.display='none'}">
    <span>打赏</span>
  </button>
  <div id="QR" style="display: none;">

    
      <div id="wechat" style="display: inline-block">
        <img id="wechat_qr" src="/img/donate/wechatpay.jpeg" alt="杨帆 微信支付"/>
        <p>微信支付</p>
      </div>
    

    
      <div id="alipay" style="display: inline-block">
        <img id="alipay_qr" src="/img/donate/alipay.jpeg" alt="杨帆 支付宝"/>
        <p>支付宝</p>
      </div>
    

    

  </div>
</div>

      </div>
    

    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/java/" rel="tag"># java</a>
          
            <a href="/tags/jdk8/" rel="tag"># jdk8</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2017/02/26/jdk8-6/" rel="next" title="JDK8-比较器Comparator">
                <i class="fa fa-chevron-left"></i> JDK8-比较器Comparator
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2017/02/26/jdk8-8/" rel="prev" title="JDK8-Collectors工厂类源码探索">
                JDK8-Collectors工厂类源码探索 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          

  
    <div class="comments" id="comments">
      <div id="lv-container" data-id="city" data-uid="MTAyMC8yOTk2Ni82NTMx"></div>
    </div>

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image"
                src="/img/photo/bug.png"
                alt="杨帆" />
            
              <p class="site-author-name" itemprop="name">杨帆</p>
              <p class="site-description motion-element" itemprop="description">记录工作和学习中遇到的问题</p>
          </div>

          
            <nav class="site-state motion-element">
              
                <div class="site-state-item site-state-posts">
                
                  <a href="/archives/">
                
                    <span class="site-state-item-count">107</span>
                    <span class="site-state-item-name">日志</span>
                  </a>
                </div>
              

              
                
                
                <div class="site-state-item site-state-categories">
                  <a href="/categories/index.html">
                    
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                    <span class="site-state-item-count">18</span>
                    <span class="site-state-item-name">分类</span>
                  </a>
                </div>
              

              
                
                
                <div class="site-state-item site-state-tags">
                  <a href="/tags/index.html">
                    
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                    <span class="site-state-item-count">38</span>
                    <span class="site-state-item-name">标签</span>
                  </a>
                </div>
              
            </nav>
          

          
            <div class="feed-link motion-element">
              <a href="/atom.xml" rel="alternate">
                <i class="fa fa-rss"></i>
                RSS
              </a>
            </div>
          

          
            <div class="links-of-author motion-element">
              
                <span class="links-of-author-item">
                  <a href="https://github.com/sail-y" target="_blank" title="GitHub"><i class="fa fa-fw fa-github"></i>GitHub</a>
                  
                </span>
              
                <span class="links-of-author-item">
                  <a href="https://weibo.com/338632221" target="_blank" title="微博"><i class="fa fa-fw fa-globe"></i>微博</a>
                  
                </span>
              
            </div>
          

          
          

          
          

          
            
          
          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#自定义收集器"><span class="nav-number">1.</span> <span class="nav-text">自定义收集器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#自定义收集器深度剖析和并行流陷阱"><span class="nav-number">2.</span> <span class="nav-text">自定义收集器深度剖析和并行流陷阱</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; 2015 &mdash; <span itemprop="copyrightYear">2018</span>
  <span class="with-love" id="animate">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">杨帆</span>

  

  
</div>




  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动 v3.7.1</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/theme-next/hexo-theme-next">NexT.Gemini</a> v6.1.0</div>




        
<div class="busuanzi-count">
  <script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

  
    <span class="site-uv" title="总访客量">
      <i class="fa fa-user"></i>
      <span class="busuanzi-value" id="busuanzi_value_site_uv"></span>
    </span>
  

  
    <span class="site-pv" title="总访问量">
      <i class="fa fa-eye"></i>
      <span class="busuanzi-value" id="busuanzi_value_site_pv"></span>
    </span>
  
</div>









        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>














  



  
  











  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/canvas-nest/canvas-nest.min.js"></script>
  

  
  
    <script type="text/javascript" src="/lib/three/three.min.js"></script>
  

  
  
    <script type="text/javascript" src="/lib/three/three-waves.min.js"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=6.1.0"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=6.1.0"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=6.1.0"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=6.1.0"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=6.1.0"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=6.1.0"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=6.1.0"></script>



  



	





  





  
    <script type="text/javascript">
      (function(d, s) {
        var j, e = d.getElementsByTagName(s)[0];
        if (typeof LivereTower === 'function') { return; }
        j = d.createElement(s);
        j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
        j.async = true;
        e.parentNode.insertBefore(j, e);
      })(document, 'script');
    </script>
  










  

  <script type="text/javascript">
    // Popup Window;
    var isfetched = false;
    var isXml = true;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length === 0) {
      search_path = "search.xml";
    } else if (/json$/i.test(search_path)) {
      isXml = false;
    }
    var path = "/" + search_path;
    // monitor main search box;

    var onPopupClose = function (e) {
      $('.popup').hide();
      $('#local-search-input').val('');
      $('.search-result-list').remove();
      $('#no-result').remove();
      $(".local-search-pop-overlay").remove();
      $('body').css('overflow', '');
    }

    function proceedsearch() {
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
        .css('overflow', 'hidden');
      $('.search-popup-overlay').click(onPopupClose);
      $('.popup').toggle();
      var $localSearchInput = $('#local-search-input');
      $localSearchInput.attr("autocapitalize", "none");
      $localSearchInput.attr("autocorrect", "off");
      $localSearchInput.focus();
    }

    // search function;
    var searchFunc = function(path, search_id, content_id) {
      'use strict';

      // start loading animation
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay">' +
          '<div id="search-loading-icon">' +
          '<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
          '</div>' +
          '</div>')
        .css('overflow', 'hidden');
      $("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');

      

      $.ajax({
        url: path,
        dataType: isXml ? "xml" : "json",
        async: true,
        success: function(res) {
          // get the contents from search data
          isfetched = true;
          $('.popup').detach().appendTo('.header-inner');
          var datas = isXml ? $("entry", res).map(function() {
            return {
              title: $("title", this).text(),
              content: $("content",this).text(),
              url: $("url" , this).text()
            };
          }).get() : res;
          var input = document.getElementById(search_id);
          var resultContent = document.getElementById(content_id);
          var inputEventFunction = function() {
            var searchText = input.value.trim().toLowerCase();
            var keywords = searchText.split(/[\s\-]+/);
            if (keywords.length > 1) {
              keywords.push(searchText);
            }
            var resultItems = [];
            if (searchText.length > 0) {
              // perform local searching
              datas.forEach(function(data) {
                var isMatch = false;
                var hitCount = 0;
                var searchTextCount = 0;
                var title = data.title.trim();
                var titleInLowerCase = title.toLowerCase();
                var content = data.content.trim().replace(/<[^>]+>/g,"");
                
                var contentInLowerCase = content.toLowerCase();
                var articleUrl = decodeURIComponent(data.url);
                var indexOfTitle = [];
                var indexOfContent = [];
                // only match articles with not empty titles
                if(title != '') {
                  keywords.forEach(function(keyword) {
                    function getIndexByWord(word, text, caseSensitive) {
                      var wordLen = word.length;
                      if (wordLen === 0) {
                        return [];
                      }
                      var startPosition = 0, position = [], index = [];
                      if (!caseSensitive) {
                        text = text.toLowerCase();
                        word = word.toLowerCase();
                      }
                      while ((position = text.indexOf(word, startPosition)) > -1) {
                        index.push({position: position, word: word});
                        startPosition = position + wordLen;
                      }
                      return index;
                    }

                    indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
                    indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
                  });
                  if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
                    isMatch = true;
                    hitCount = indexOfTitle.length + indexOfContent.length;
                  }
                }

                // show search results

                if (isMatch) {
                  // sort index by position of keyword

                  [indexOfTitle, indexOfContent].forEach(function (index) {
                    index.sort(function (itemLeft, itemRight) {
                      if (itemRight.position !== itemLeft.position) {
                        return itemRight.position - itemLeft.position;
                      } else {
                        return itemLeft.word.length - itemRight.word.length;
                      }
                    });
                  });

                  // merge hits into slices

                  function mergeIntoSlice(text, start, end, index) {
                    var item = index[index.length - 1];
                    var position = item.position;
                    var word = item.word;
                    var hits = [];
                    var searchTextCountInSlice = 0;
                    while (position + word.length <= end && index.length != 0) {
                      if (word === searchText) {
                        searchTextCountInSlice++;
                      }
                      hits.push({position: position, length: word.length});
                      var wordEnd = position + word.length;

                      // move to next position of hit

                      index.pop();
                      while (index.length != 0) {
                        item = index[index.length - 1];
                        position = item.position;
                        word = item.word;
                        if (wordEnd > position) {
                          index.pop();
                        } else {
                          break;
                        }
                      }
                    }
                    searchTextCount += searchTextCountInSlice;
                    return {
                      hits: hits,
                      start: start,
                      end: end,
                      searchTextCount: searchTextCountInSlice
                    };
                  }

                  var slicesOfTitle = [];
                  if (indexOfTitle.length != 0) {
                    slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
                  }

                  var slicesOfContent = [];
                  while (indexOfContent.length != 0) {
                    var item = indexOfContent[indexOfContent.length - 1];
                    var position = item.position;
                    var word = item.word;
                    // cut out 100 characters
                    var start = position - 20;
                    var end = position + 80;
                    if(start < 0){
                      start = 0;
                    }
                    if (end < position + word.length) {
                      end = position + word.length;
                    }
                    if(end > content.length){
                      end = content.length;
                    }
                    slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
                  }

                  // sort slices in content by search text's count and hits' count

                  slicesOfContent.sort(function (sliceLeft, sliceRight) {
                    if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
                      return sliceRight.searchTextCount - sliceLeft.searchTextCount;
                    } else if (sliceLeft.hits.length !== sliceRight.hits.length) {
                      return sliceRight.hits.length - sliceLeft.hits.length;
                    } else {
                      return sliceLeft.start - sliceRight.start;
                    }
                  });

                  // select top N slices in content

                  var upperBound = parseInt('1');
                  if (upperBound >= 0) {
                    slicesOfContent = slicesOfContent.slice(0, upperBound);
                  }

                  // highlight title and content

                  function highlightKeyword(text, slice) {
                    var result = '';
                    var prevEnd = slice.start;
                    slice.hits.forEach(function (hit) {
                      result += text.substring(prevEnd, hit.position);
                      var end = hit.position + hit.length;
                      result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
                      prevEnd = end;
                    });
                    result += text.substring(prevEnd, slice.end);
                    return result;
                  }

                  var resultItem = '';

                  if (slicesOfTitle.length != 0) {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
                  } else {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
                  }

                  slicesOfContent.forEach(function (slice) {
                    resultItem += "<a href='" + articleUrl + "'>" +
                      "<p class=\"search-result\">" + highlightKeyword(content, slice) +
                      "...</p>" + "</a>";
                  });

                  resultItem += "</li>";
                  resultItems.push({
                    item: resultItem,
                    searchTextCount: searchTextCount,
                    hitCount: hitCount,
                    id: resultItems.length
                  });
                }
              })
            };
            if (keywords.length === 1 && keywords[0] === "") {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
            } else if (resultItems.length === 0) {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
            } else {
              resultItems.sort(function (resultLeft, resultRight) {
                if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
                  return resultRight.searchTextCount - resultLeft.searchTextCount;
                } else if (resultLeft.hitCount !== resultRight.hitCount) {
                  return resultRight.hitCount - resultLeft.hitCount;
                } else {
                  return resultRight.id - resultLeft.id;
                }
              });
              var searchResultList = '<ul class=\"search-result-list\">';
              resultItems.forEach(function (result) {
                searchResultList += result.item;
              })
              searchResultList += "</ul>";
              resultContent.innerHTML = searchResultList;
            }
          }

          if ('auto' === 'auto') {
            input.addEventListener('input', inputEventFunction);
          } else {
            $('.search-icon').click(inputEventFunction);
            input.addEventListener('keypress', function (event) {
              if (event.keyCode === 13) {
                inputEventFunction();
              }
            });
          }

          // remove loading animation
          $(".local-search-pop-overlay").remove();
          $('body').css('overflow', '');

          proceedsearch();
        }
      });
    }

    // handle and trigger popup window;
    $('.popup-trigger').click(function(e) {
      e.stopPropagation();
      if (isfetched === false) {
        searchFunc(path, 'local-search-input', 'local-search-result');
      } else {
        proceedsearch();
      };
    });

    $('.popup-btn-close').click(onPopupClose);
    $('.popup').click(function(e){
      e.stopPropagation();
    });
    $(document).on('keyup', function (event) {
      var shouldDismissSearchPopup = event.which === 27 &&
        $('.search-popup').is(':visible');
      if (shouldDismissSearchPopup) {
        onPopupClose();
      }
    });
  </script>





  

  

  

  

  
  

  

  

  

  

</body>
</html>
